package cn.xo68.boot.job;

import cn.xo68.boot.job.jdbcstore.JobStoreService;
import cn.xo68.boot.job.jdbcstore.LogStoreService;
import cn.xo68.boot.job.jdbcstore.impl.JobStoreServiceImpl;
import cn.xo68.boot.job.jdbcstore.impl.LogStoreServiceImpl;
import cn.xo68.boot.job.jdbcstore.lock.DistributedLockManager;
import cn.xo68.boot.job.jdbcstore.lock.DistributedLockStore;
import cn.xo68.boot.job.jdbcstore.lock.impl.DistributedLockStoreImpl;
import cn.xo68.boot.job.listener.JobStateListener;
import cn.xo68.boot.job.listener.SchedulerStatusListener;
import cn.xo68.boot.job.listener.TriggerStatusListener;
import cn.xo68.boot.job.properties.JobDataSourceProperties;
import cn.xo68.boot.job.properties.JobProperties;
import cn.xo68.boot.job.util.JobUtils;
import cn.xo68.core.jdbc.JdbcOperate;
import cn.xo68.core.jdbc.impl.JdbcOperateDefaultImpl;
import cn.xo68.core.util.JsonUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.quartz.Scheduler;
import org.quartz.spi.JobFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.util.Properties;

@Configuration
@EnableConfigurationProperties({JobProperties.class, JobDataSourceProperties.class, ServerProperties.class})
@ConditionalOnProperty(prefix = "wuxie.job",value = "enabled",havingValue = "true", matchIfMissing=true)
@ComponentScan({"cn.xo68.boot.job.config",
        "cn.xo68.boot.job.mapper",
"cn.xo68.boot.job.jdbcstore","cn.xo68.boot.job.service","cn.xo68.boot.job.controller"})
@Import({JobSchedulerHeartBeat.class,
        JobMonitor.class,
        JobSchedulerRegisterAndOperate.class,
        DistributedLockManager.class})
public class JobAutoConfiguration {

    private static final Logger logger=LoggerFactory.getLogger(JobAutoConfiguration.class);



    @ConditionalOnMissingBean(JsonUtil.class)
    @Bean
    public JsonUtil jsonUtil(ObjectMapper objectMapper){
        return new JsonUtil(objectMapper);
    }

    @Bean
    public JdbcOperate jdbcOperate(JdbcTemplate jdbcTemplate){
        return new JdbcOperateDefaultImpl(jdbcTemplate);
    }

    @Bean
    public QuartzJobFactory quartzJobFactoryInstance(ApplicationContext applicationContext) {
        QuartzJobFactory jobFactory = new QuartzJobFactory();
        jobFactory.setApplicationContext(applicationContext);
        return jobFactory;
    }

    @Autowired(required = false)
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource,JobFactory jobFactory, JobProperties jobProperties, ServerProperties serverProperties,JobStoreService jobStoreService) {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        if(!StringUtils.isEmpty(jobProperties.getSchedulerName())){
            factory.setSchedulerName(jobProperties.getSchedulerName());
        }

        Properties properties=new Properties();
        String instanceName= JobUtils.generateSchedulerInstanceName(jobProperties.getIpAddress(),serverProperties.getPort());
        properties.setProperty("org.quartz.scheduler.instanceId",instanceName);

        factory.setQuartzProperties(properties);

        factory.setDataSource(dataSource);
        factory.setAutoStartup(false);
        factory.setStartupDelay(jobProperties.getStartupDelay());
        factory.setConfigLocation(new ClassPathResource(jobProperties.getQuartzConfigLocation()));
        factory.setOverwriteExistingJobs(jobProperties.isOverwriteExistingJobs());
        factory.setJobFactory(jobFactory);
        factory.setApplicationContextSchedulerContextKey(jobProperties.getApplicationContextSchedulerContextKey());

        SchedulerStatusListener schedulerStatusListener=new SchedulerStatusListener(jobStoreService,jobProperties,serverProperties);
        factory.setSchedulerListeners(schedulerStatusListener);

        TriggerStatusListener triggerStatusListener=new TriggerStatusListener();
        factory.setGlobalTriggerListeners(triggerStatusListener);

        JobStateListener jobStateListener=new JobStateListener();
        factory.setGlobalJobListeners(jobStateListener);

        return factory;
    }


    /**
     * 通过SchedulerFactoryBean获取Scheduler的实例
     * @param schedulerFactoryBean
     * @return
     */
    @Bean
    public Scheduler scheduler(SchedulerFactoryBean schedulerFactoryBean){
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        return scheduler;
    }

    @Autowired(required = false)
    @Bean
    public JobOperateService jobOperateService(Scheduler scheduler, JsonUtil jsonUtil){
        return new JobOperateService(scheduler, jsonUtil);
    }


    @ConditionalOnMissingBean(DistributedLockStore.class)
    @Bean
    public DistributedLockStore distributedLockManage(@Qualifier("jdbcOperate")JdbcOperate jdbcOperate){
        return new DistributedLockStoreImpl(jdbcOperate);
    }
    @ConditionalOnMissingBean(JobStoreService.class)
    @Bean
    public JobStoreService jobStoreService(@Qualifier("jdbcOperate")JdbcOperate jdbcOperate){
        return new JobStoreServiceImpl(jdbcOperate);
    }

    @ConditionalOnMissingBean(LogStoreService.class)
    @Bean
    public LogStoreService logStoreService(@Qualifier("jdbcOperate")JdbcOperate jdbcOperate){
        return new LogStoreServiceImpl(jdbcOperate);
    }
}
